import pandas as pd
import numpy as np
import logging
from datetime import datetime

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

class DataPreprocessor:
    """
    数据预处理类
    用于对新浪财经历史分红数据进行预处理
    """
    
    def __init__(self, input_file='新浪财经历史分红数据.xlsx', output_file='新浪财经历史分红数据_预处理.xlsx'):
        """
        初始化预处理器
        
        参数:
            input_file: 输入的Excel文件名
            output_file: 输出的Excel文件名
        """
        self.input_file = input_file
        self.output_file = output_file
        self.df = None
    
    def load_data(self):
        """
        加载数据
        
        返回:
            是否加载成功
        """
        try:
            self.df = pd.read_excel(self.input_file)
            logging.info(f'成功加载数据文件{self.input_file}')
            logging.info(f'数据包含的列: {self.df.columns.tolist()}')
            return True
        except Exception as e:
            logging.error(f'加载数据文件时发生错误: {str(e)}')
            return False
    
    def check_data_shape(self):
        """
        检查数据形状
        """
        if self.df is None:
            logging.error('数据尚未加载')
            return
        
        shape = self.df.shape
        logging.info(f'数据形状: {shape[0]}行, {shape[1]}列')
        return shape
    
    def check_duplicates(self, columns=['代码', '名称', '详细']):
        """
        检查指定列的重复值
        
        参数:
            columns: 要检查重复值的列名列表
        """
        if self.df is None:
            logging.error('数据尚未加载')
            return
        
        for col in columns:
            if col in self.df.columns:
                duplicates = self.df[col].duplicated().sum()
                logging.info(f'列{col}中的重复值数量: {duplicates}')
            else:
                logging.warning(f'列{col}不存在于数据中，尝试查找相似列名')
                # 查找相似的列名
                similar_cols = [c for c in self.df.columns if col in c]
                if similar_cols:
                    logging.info(f'找到相似的列名: {similar_cols}')
                    for similar_col in similar_cols:
                        duplicates = self.df[similar_col].duplicated().sum()
                        logging.info(f'列{similar_col}中的重复值数量: {duplicates}')
    
    def show_head(self, n=5):
        """
        展示数据前n行
        
        参数:
            n: 要展示的行数
        """
        if self.df is None:
            logging.error('数据尚未加载')
            return
        
        logging.info(f'数据前{n}行:')
        print(self.df.head(n))
        return self.df.head(n)
    
    def process_listed_date(self, date_column=None):
        """
        处理上市日期，提取年、月、日变量
        
        参数:
            date_column: 日期列名，如果为None则自动查找
        """
        if self.df is None:
            logging.error('数据尚未加载')
            return
        
        # 如果没有指定日期列名，尝试自动查找
        if date_column is None:
            # 查找包含'上市'或'日期'的列
            date_cols = [col for col in self.df.columns if '上市' in col or '日期' in col]
            if date_cols:
                date_column = date_cols[0]
                logging.info(f'自动选择日期列: {date_column}')
            else:
                logging.warning('未找到包含\'上市\'或\'日期\'的列')
                return
        
        if date_column in self.df.columns:
            try:
                # 尝试转换为日期类型
                self.df[date_column] = pd.to_datetime(self.df[date_column], errors='coerce')
                
                # 统计转换失败的数量
                failed_count = self.df[date_column].isna().sum()
                if failed_count > 0:
                    logging.warning(f'日期转换失败{failed_count}条记录')
                
                # 提取年、月、日
                self.df[f'{date_column}_年'] = self.df[date_column].dt.year
                self.df[f'{date_column}_月'] = self.df[date_column].dt.month
                self.df[f'{date_column}_日'] = self.df[date_column].dt.day
                
                logging.info(f'成功处理{date_column}列，提取了年、月、日变量')
            except Exception as e:
                logging.error(f'处理{date_column}列时发生错误: {str(e)}')
        else:
            logging.warning(f'列{date_column}不存在于数据中')
    
    def normalize_numeric_columns(self, numeric_columns=None):
        """
        对数值型变量进行归一化处理
        
        参数:
            numeric_columns: 要归一化的数值列名列表，如果为None则自动查找
        """
        if self.df is None:
            logging.error('数据尚未加载')
            return
        
        # 如果没有指定数值列，尝试自动查找
        if numeric_columns is None:
            # 查找包含股息、分红、融资等关键字的列
            keywords = ['股息', '分红', '融资', '累计', '年均']
            numeric_columns = []
            for col in self.df.columns:
                if any(keyword in col for keyword in keywords):
                    numeric_columns.append(col)
            logging.info(f'自动选择数值列: {numeric_columns}')
        
        for col in numeric_columns:
            if col in self.df.columns:
                try:
                    # 尝试转换为数值类型
                    # 先处理可能的百分比符号
                    if '%' in col:
                        # 如果列中包含'%'符号，先去除再转换
                        self.df[col] = self.df[col].astype(str).str.replace('%', '')
                    self.df[col] = pd.to_numeric(self.df[col], errors='coerce')
                    
                    # 统计转换失败的数量
                    failed_count = self.df[col].isna().sum()
                    if failed_count > 0:
                        logging.warning(f'{col}列数值转换失败{failed_count}条记录')
                    
                    # 归一化处理 (Min-Max归一化)
                    min_val = self.df[col].min()
                    max_val = self.df[col].max()
                    
                    if max_val > min_val:  # 避免除以0
                        self.df[f'{col}_归一化'] = (self.df[col] - min_val) / (max_val - min_val)
                        logging.info(f'成功对{col}列进行归一化处理')
                    else:
                        logging.warning(f'{col}列的所有值相同，无法进行归一化')
                except Exception as e:
                    logging.error(f'归一化{col}列时发生错误: {str(e)}')
            else:
                logging.warning(f'列{col}不存在于数据中，尝试查找相似列名')
                # 查找相似的列名
                similar_cols = [c for c in self.df.columns if any(kw in c for kw in ['股息', '分红', '融资'])]
                if similar_cols:
                    logging.info(f'找到相似的数值列: {similar_cols}')
                    for similar_col in similar_cols:
                        try:
                            # 尝试转换为数值类型
                            if '%' in similar_col:
                                self.df[similar_col] = self.df[similar_col].astype(str).str.replace('%', '')
                            self.df[similar_col] = pd.to_numeric(self.df[similar_col], errors='coerce')
                            
                            # 归一化处理
                            min_val = self.df[similar_col].min()
                            max_val = self.df[similar_col].max()
                            
                            if max_val > min_val:
                                self.df[f'{similar_col}_归一化'] = (self.df[similar_col] - min_val) / (max_val - min_val)
                                logging.info(f'成功对{similar_col}列进行归一化处理')
                        except Exception as e:
                            logging.error(f'归一化{similar_col}列时发生错误: {str(e)}')
    
    def data_quality_check(self):
        """
        执行数据质量检查
        """
        if self.df is None:
            logging.error('数据尚未加载')
            return
        
        # 检查缺失值
        missing_values = self.df.isnull().sum()
        if missing_values.sum() > 0:
            logging.warning(f'数据中存在{missing_values.sum()}个缺失值')
            # 只显示有缺失值的列
            cols_with_missing = missing_values[missing_values > 0]
            for col, count in cols_with_missing.items():
                logging.warning(f'列{col}中缺失值数量: {count} ({count/len(self.df)*100:.2f}%)')
        else:
            logging.info('数据中没有缺失值')
    
    def save_preprocessed_data(self):
        """
        保存预处理后的数据
        """
        if self.df is None:
            logging.error('数据尚未加载或处理')
            return
        
        try:
            self.df.to_excel(self.output_file, index=False)
            logging.info(f'预处理后的数据已成功保存到{self.output_file}')
        except Exception as e:
            logging.error(f'保存预处理后的数据时发生错误: {str(e)}')
    
    def run(self):
        """
        运行预处理的主方法
        """
        logging.info('开始数据预处理')
        
        # 加载数据
        if not self.load_data():
            return
        
        # 检查数据形状
        self.check_data_shape()
        
        # 执行数据质量检查
        self.data_quality_check()
        
        # 检查重复值
        self.check_duplicates(columns=['代码', '名称', '详细'])
        
        # 展示数据前5行
        self.show_head()
        
        # 处理上市日期
        self.process_listed_date()
        
        # 归一化数值型变量
        # 尝试匹配各种可能的列名格式
        numeric_columns_options = [
            ['累计股息(%)', '年均股息(%)', '分红次数', '融资总额(亿元)'],
            ['累计股息（%）', '年均股息（%）', '分红次数', '融资总额（亿元）']
        ]
        
        # 先尝试自动检测，如果没有检测到再使用预设列名
        self.normalize_numeric_columns()
        
        # 保存预处理后的数据
        self.save_preprocessed_data()
        
        logging.info('数据预处理完成')

if __name__ == '__main__':
    # 创建预处理器实例并运行
    preprocessor = DataPreprocessor()
    preprocessor.run()